package com.leyou.search.service.impl;

import com.leyou.common.dto.PageDTO;
import com.leyou.common.exceptions.LyException;
import com.leyou.item.client.ItemClient;
import com.leyou.item.dto.SkuDTO;
import com.leyou.item.dto.SpecParamDTO;
import com.leyou.item.dto.SpuDTO;
import com.leyou.search.constants.SearchConstants;
import com.leyou.search.dto.SearchParamDTO;
import com.leyou.search.entity.Goods;
import com.leyou.search.repository.GoodsRepository;
import com.leyou.search.service.SearchService;
import com.leyou.starter.elastic.dto.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import reactor.core.publisher.Mono;

import java.util.*;
import java.util.stream.Collectors;

import static com.leyou.search.constants.SearchConstants.DEFAULT_SEARCH_FIELD;
import static com.leyou.search.constants.SearchConstants.INDEX_MAPPING_SOURCE;

/**
 * @author 虎哥
 */
@Slf4j
@Service
public class SearchServiceImpl implements SearchService {

    private GoodsRepository repository;
    private ItemClient itemClient;

    public SearchServiceImpl(GoodsRepository repository, ItemClient itemClient) {
        this.repository = repository;
        this.itemClient = itemClient;
    }

    @Override
    public void createIndexAndMapping() {
        try {
            // 尝试删除索引库
            repository.deleteIndex();
        } catch (Exception e) {
            log.info("索引库可能不存在", e);
        }
        // 创建索引库
        repository.createIndex(INDEX_MAPPING_SOURCE);
        log.info("索引库初始化完成！");
    }

    @Override
    public void loadData() {
        // 分页信息
        int page = 1, rows = 50;
        log.info("准备导入数据到索引库");
        while (true){
            // 批量查询spu
            PageDTO<SpuDTO> result = itemClient.querySpuByPage(page, rows, true, null, null, null);
            log.info("开始导入第{}页商品数据", page);
            List<SpuDTO> list = result.getItems();
            // 循环处理
            List<Goods> goodsList = list.stream().map(this::buildGoods).collect(Collectors.toList());
            /*List<Goods> goodsList = new ArrayList<>();
            for (SpuDTO spu : list) {
                // 构建goods对象，需要一个SpuDto
                Goods goods = buildGoods(spu);
                goodsList.add(goods);
            }*/
            // 批量保存到索引库
            repository.saveAll(goodsList);
            log.info("导入第{}页商品数据完成！", page);
            if(page >= result.getTotalPage()){
                break;
            }
            // 翻页
            page++;
        }
    }

    @Override
    public Mono<List<String>> getSuggestion(String keyPrefix) {
        return repository.suggestBySingleField(SearchConstants.DEFAULT_SUGGEST_FIELD, keyPrefix);
    }

    @Override
    public Mono<PageInfo<Goods>> search(SearchParamDTO param) {
        // 搜索条件的构建器
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        // 0.source过滤
        sourceBuilder.fetchSource(new String[]{"id", "title", "image", "prices", "sold"}, null);
        // 1.搜索条件
        String key = param.getKey();
        if (StringUtils.isBlank(key)) {
            throw new LyException(404, "查询商品不存在！");
        }
        sourceBuilder.query(QueryBuilders.matchQuery(DEFAULT_SEARCH_FIELD, key).operator(Operator.OR));

        // 2.分页条件
        int page = param.getPage();
        int size = param.getSize();
        sourceBuilder.from((page - 1) * size);
        sourceBuilder.size(size);

        // 3.排序条件
        String sortBy = param.getSortBy();
        if(StringUtils.isNotBlank(sortBy)){
            // 排序
            sourceBuilder.sort(sortBy, param.getDesc() ? SortOrder.DESC : SortOrder.ASC);
        }

        // 4.高亮条件
        sourceBuilder.highlighter(new HighlightBuilder().field(DEFAULT_SEARCH_FIELD).preTags("<am>").postTags("</am>"));

        return repository.queryBySourceBuilderForPageHighlight(sourceBuilder);
    }

    @Override
    public void saveSpuById(Long id) {
        // 根据id查询spu
        SpuDTO spu = itemClient.queryGoodsById(id);
        // 得到goods
        Goods goods = buildGoods(spu);
        // 新增
        repository.save(goods);
    }

    @Override
    public void deleteSpuById(Long id) {
        repository.deleteById(id);
    }

    private Goods buildGoods(SpuDTO spu) {
        // 1.自动补全,商品名称、分类名称、品牌名称
        // 1.1.分类名称
        String[] categoryNames = StringUtils.split(spu.getCategoryName(), "/");
        List<String> suggestions = new ArrayList<>();
        // 1.2.商品名称
        suggestions.add(spu.getName());
        // 1.3.品牌名称
        suggestions.add(spu.getBrandName());
        Collections.addAll(suggestions, categoryNames);

        // 2.搜索标题、包含商品标题、分类和品牌名称
        String title = spu.getTitle() + spu.getBrandName() + spu.getCategoryName();

        // 3.sku相关
        // 3.1.查询sku
        List<SkuDTO> skuList = spu.getSkus();
        if(CollectionUtils.isEmpty(skuList)){
            skuList = itemClient.querySkuBySpuId(spu.getId());
        }
        // 3.2.销量
        long sold = skuList.stream().mapToLong(SkuDTO::getSold).sum();
        // 3.3.价格
        Set<Long> prices = skuList.stream().map(SkuDTO::getPrice).collect(Collectors.toSet());
        // 3.4.图片
        String image = StringUtils.substringBefore(skuList.get(0).getImages(), ",");

        // 4.规格参数键值对
        List<Map<String,Object>> specs = new ArrayList<>();
        // 4.1.查询当前商品的规格参数键值对
        List<SpecParamDTO> paramList = itemClient.querySpecsValues(spu.getId(), true);
        // 4.2.转换SpecParamDTO为map
        for (SpecParamDTO paramDTO : paramList) {
            Map<String,Object> map = new HashMap<>(2);
            map.put("name", paramDTO.getName());
            map.put("value", chooseSegment(paramDTO));
            specs.add(map);
        }

        // 创建Goods
        Goods goods = new Goods();
        // 填数据
        goods.setUpdateTime(new Date());
        // 自动补全， 商品名称、分类名称、品牌名称
        goods.setSuggestion(suggestions);
        // 规格参数键值对
        goods.setSpecs(specs);
        // 销量
        goods.setSold(sold);
        // 搜索标题、包含商品标题、分类和品牌名称
        goods.setTitle(title);
        // 商品价格，各个sku的价格集合
        goods.setPrices(prices);
        // 商品的某个sku的图片
        goods.setImage(image);
        goods.setCategoryId(spu.getCid3());
        goods.setBrandId(spu.getBrandId());
        goods.setId(spu.getId());

        return goods;
    }

    private Object chooseSegment(SpecParamDTO p) {
        Object value = p.getValue();
        if (value == null || StringUtils.isBlank(value.toString())) {
            return "其它";
        }
        if(!p.getNumeric() || StringUtils.isBlank(p.getSegments()) || value instanceof Collection){
            return value;
        }
        double val = parseDouble(value.toString());
        String result = "其它";
        // 保存数值段
        for (String segment : p.getSegments().split(",")) {
            String[] segs = segment.split("-");
            // 获取数值范围
            double begin = parseDouble(segs[0]);
            double end = Double.MAX_VALUE;
            if (segs.length == 2) {
                end = parseDouble(segs[1]);
            }
            // 判断是否在范围内
            if (val >= begin && val < end) {
                if (segs.length == 1) {
                    result = segs[0] + p.getUnit() + "以上";
                } else if (begin == 0) {
                    result = segs[1] + p.getUnit() + "以下";
                } else {
                    result = segment + p.getUnit();
                }
                break;
            }
        }
        return result;
    }

    private double parseDouble(String str) {
        try {
            return Double.parseDouble(str);
        } catch (Exception e) {
            return 0;
        }
    }
}
